import os
import sys
import argparse
import subprocess
from datetime import datetime
from typing import List, Dict, Any
from constants import (
    WUSA_PATH,
    DISM_PATH,
    SUPPORTED_FILE_TYPES,
    SUCCESS_EXIT_CODES,
    MAX_PROCESS_OUTPUT_LINES,
    APP_NAME,
    APP_VERSION,
    APP_DESCRIPTION,
    SHUTDOWN_PATH,
    DEFAULT_REBOOT_DELAY
)
from utils import (
    format_exit_code_cli,
    describe_wusa_code_cli,
    describe_exe_code_cli,
    describe_dism_code_cli,
    collect_failure_logs_cli
)

def parse_args(args: List[str]) -> argparse.Namespace:
    """解析命令行参数"""
    parser = argparse.ArgumentParser(
        description=APP_DESCRIPTION,
        formatter_class=argparse.RawTextHelpFormatter
    )
    parser.add_argument(
        "--dir", 
        "-d",
        help="指定补丁目录路径，默认使用当前目录",
        default=os.getcwd()
    )
    parser.add_argument(
        "--norestart", 
        action="store_true",
        help="安装完成后不提示重启（默认会提示）"
    )
    parser.add_argument(
        "--logfile", 
        "-l",
        help="指定日志文件路径，默认在补丁目录下生成"
    )
    
    # 如果没有提供参数，返回默认值
    if len(args) == 0:
        return parser.parse_args([])
    
    return parser.parse_args(args)

def run_cli_mode(args: List[str] = None) -> int:
    """命令行模式安装补丁
    
    Args:
        args: 命令行参数列表
        
    Returns:
        int: 退出代码，0表示成功，非0表示失败
    """
    # 解析命令行参数
    parsed_args = parse_args(args or [])
    base_dir = parsed_args.dir
    no_restart = parsed_args.norestart
    custom_log_file = parsed_args.logfile
    
    # 验证目录是否存在
    if not os.path.isdir(base_dir):
        print(f"错误：目录不存在或无法访问：{base_dir}")
        return 1
    
    # 设置日志文件路径
    log_file = custom_log_file or os.path.join(base_dir, f"{datetime.now().strftime('%Y%m%d')}_PatchInstall_CLI.log")
    
    # 创建日志目录（如果自定义日志路径）
    if custom_log_file:
        log_dir = os.path.dirname(log_file)
        if log_dir and not os.path.exists(log_dir):
            try:
                os.makedirs(log_dir)
            except Exception as e:
                print(f"错误：无法创建日志目录：{e}")
                return 1

    def log_line(msg: str, level: str = "info"):
        """记录日志到控制台和文件
        
        Args:
            msg: 日志消息
            level: 日志级别 (info, success, warning, error)
        """
        ts = datetime.now().strftime("%H:%M:%S")
        level_str = f"[{level.upper()}] " if level != "info" else ""
        line = f"[{ts}] {level_str}{msg}"
        print(line)
        try:
            with open(log_file, "a", encoding="utf-8") as f:
                f.write(line + "\n")
        except Exception as e:
            print(f"警告：无法写入日志文件：{e}")

    try:
        # 加载补丁文件，跳过自身（仅在打包为EXE时）
        self_exe_path = os.path.abspath(sys.executable) if getattr(sys, "frozen", False) else None
        patch_files = []
        
        # 显示支持的文件类型信息
        supported_types_str = ", ".join([f".{t}" for t in SUPPORTED_FILE_TYPES])
        log_line(f"开始扫描目录：{base_dir}")
        log_line(f"支持的补丁类型：{supported_types_str}")
        
        for file in sorted(os.listdir(base_dir)):
            if file.lower().endswith(SUPPORTED_FILE_TYPES):
                full_path = os.path.join(base_dir, file)
                if self_exe_path and os.path.abspath(full_path) == self_exe_path:
                    log_line(f"跳过自身文件：{file}", "warning")
                    continue
                patch_files.append(full_path)
                log_line(f"发现补丁文件：{file}")
    except Exception as e:
        log_line(f"扫描目录失败：{e}", "error")
        return 1

    total = len(patch_files)
    if total == 0:
        log_line(f"当前目录未找到任何 {supported_types_str} 补丁文件", "warning")
        return 0

    log_line(f"命令行模式开始安装，共 {total} 个补丁", "info")
    success = 0
    failure = 0
    reboot_needed = 0
    
    # 检查必要的系统工具是否可用
    missing_tools = []
    if any(patch.endswith(".msu") for patch in patch_files) and not os.path.isfile(WUSA_PATH):
        missing_tools.append(f"wusa.exe ({WUSA_PATH})")
    if any(patch.endswith(".cab") for patch in patch_files) and not os.path.isfile(DISM_PATH):
        missing_tools.append(f"dism.exe ({DISM_PATH})")
    
    if missing_tools:
        for tool in missing_tools:
            log_line(f"警告：未找到必要的系统工具：{tool}", "warning")
        log_line("继续安装，但相关类型的补丁可能会失败", "warning")

    for i, patch_path in enumerate(patch_files, 1):
        name = os.path.basename(patch_path)
        progress = f"[{i}/{total}]"
        
        if not os.path.isfile(patch_path):
            failure += 1
            log_line(f"{progress} 路径不存在：{patch_path}", "error")
            continue
            
        log_line(f"{progress} 开始安装：{name}", "info")
        patch_log = os.path.join(base_dir, f"{datetime.now().strftime('%Y%m%d')}_{os.path.splitext(name)[0]}.log")
        ext = os.path.splitext(name)[1].lower()
        cmd = None
        
        try:
            # 确定安装命令
            if ext == ".msu":
                if not os.path.isfile(WUSA_PATH):
                    failure += 1
                    log_line(f"{progress} 未找到 wusa.exe：{WUSA_PATH}", "error")
                    continue
                cmd = [WUSA_PATH, patch_path, "/quiet", "/norestart", f"/log:{patch_log}"]
                log_line(f"{progress} 使用wusa.exe安装MSU补丁", "info")
                
            elif ext == ".cab":
                if not os.path.isfile(DISM_PATH):
                    failure += 1
                    log_line(f"{progress} 未找到 dism.exe：{DISM_PATH}", "error")
                    continue
                abs_patch = os.path.abspath(patch_path)
                cmd = [
                    DISM_PATH,
                    "/online",
                    "/add-package",
                    f"/packagepath:{abs_patch}",
                    "/norestart",
                    f"/logpath:{patch_log}",
                ]
                log_line(f"{progress} 使用dism.exe安装CAB补丁", "info")
                
            elif ext == ".exe":
                cmd = [patch_path, "/quiet", "/norestart"]
                log_line(f"{progress} 执行EXE安装程序", "info")
                
            else:
                failure += 1
                log_line(f"{progress} 不支持的文件类型：{name}", "error")
                continue

            # 记录执行的命令
            log_line(f"{progress} 执行命令：{' '.join(cmd)}", "info")
            
            # 执行命令
            res = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0),
                timeout=None  # 不设置超时，让补丁有足够时间安装
            )
            
            # 输出命令执行结果（限制长度，避免输出过多）
            if res.stdout and res.stdout.strip():
                stdout_lines = res.stdout.strip().split('\n')
                if len(stdout_lines) > MAX_PROCESS_OUTPUT_LINES:
                    log_line(f"{progress} 标准输出（前{MAX_PROCESS_OUTPUT_LINES}行）：\n" + "\n".join(stdout_lines[:MAX_PROCESS_OUTPUT_LINES]), "info")
                    log_line(f"{progress} ... 更多输出已省略（共{len(stdout_lines)}行）", "info")
                else:
                    log_line(f"{progress} 标准输出：{res.stdout.strip()}", "info")
            
            if res.stderr and res.stderr.strip():
                stderr_lines = res.stderr.strip().split('\n')
                if len(stderr_lines) > MAX_PROCESS_OUTPUT_LINES:
                    log_line(f"{progress} 错误输出（前{MAX_PROCESS_OUTPUT_LINES}行）：\n" + "\n".join(stderr_lines[:MAX_PROCESS_OUTPUT_LINES]), "error")
                    log_line(f"{progress} ... 更多错误输出已省略（共{len(stderr_lines)}行）", "error")
                else:
                    log_line(f"{progress} 错误输出：{res.stderr.strip()}", "error")

            # 处理返回码
            rc = res.returncode
            if rc == 0:
                success += 1
                log_line(f"{progress} 安装成功：{name}（无需重启）", "success")
            elif rc in SUCCESS_EXIT_CODES:
                success += 1
                reboot_needed += 1
                log_line(f"{progress} 安装成功：{name}（需重启生效）", "success")
            else:
                failure += 1
                # 根据文件类型选择相应的错误描述函数
                try:
                    if ext == ".exe":
                        desc = describe_exe_code_cli(rc)
                    elif ext == ".cab":
                        desc = describe_dism_code_cli(rc)
                    else:  # .msu
                        desc = describe_wusa_code_cli(rc)
                except Exception:
                    desc = "未知错误"
                
                log_line(f"{progress} 安装失败：{name}，返回码：{format_exit_code_cli(rc)}，{desc}", "error")
                log_line(f"{progress} 详细日志：{os.path.basename(patch_log)}", "error")
                
                # 收集失败日志
                try:
                    dst = collect_failure_logs_cli(base_dir, name, patch_log, " ".join(cmd), rc)
                    if dst:
                        log_line(f"{progress} 已归档失败日志：{dst}", "info")
                except Exception as e:
                    log_line(f"{progress} 收集失败日志时出错：{e}", "error")
        except subprocess.TimeoutExpired:
            failure += 1
            log_line(f"{progress} 安装超时：{name}", "error")
            try:
                if cmd:
                    dst = collect_failure_logs_cli(base_dir, name, patch_log, " ".join(cmd), -1)
                    if dst:
                        log_line(f"{progress} 已归档超时日志：{dst}", "info")
            except Exception:
                pass
        
        except KeyboardInterrupt:
            log_line(f"{progress} 安装被用户中断", "warning")
            return 2  # 返回特殊退出码表示用户中断
            
        except Exception as e:
            failure += 1
            log_line(f"{progress} 安装异常：{name}，{str(e)}", "error")
            try:
                cmd_str = " ".join(cmd) if cmd else ""
                dst = collect_failure_logs_cli(base_dir, name, patch_log, cmd_str)
                if dst:
                    log_line(f"{progress} 已归档异常日志：{dst}", "info")
            except Exception as log_error:
                log_line(f"{progress} 收集异常日志时出错：{log_error}", "error")

    # 输出安装总结
    summary = f"安装结束：成功 {success}，失败 {failure}，需重启 {reboot_needed}"
    log_line(summary, "success" if failure == 0 else "warning")
    
    # 输出重启提示
    if reboot_needed and not no_restart:
        log_line("提示：部分补丁需要重启才能生效", "warning")
        log_line("如需重启系统，请执行：shutdown /r /t 30", "warning")
    
    # 输出日志文件位置
    log_line(f"详细日志已保存到：{log_file}", "info")
    
    # 返回退出码
    if failure > 0:
        return 1  # 有失败的补丁
    return 0  # 全部成功

if __name__ == "__main__":
    # 直接运行时的入口
    exit_code = run_cli_mode(sys.argv[1:])
    sys.exit(exit_code)